let app = getApp()
let config = app.config
let crypto = require('./crypto')

class util {

    constructor() {
		this.msgBack = {}
        this.tipCloseTimer = null
		this.heartTimer = null
		this.wsConnTryCount = 0
		this.requestSources = []
    }

	isJson(obj) {
		try {
			return JSON.parse(obj)
		}
		catch(err) {
			console.error(err)
			return null
		}
	}

	unloadPage(pageName) {
		const sIndex = this.requestSources.indexOf(pageName)
		if (sIndex != -1)
			this.requestSources.splice(sIndex, 1)
	}

    config(key, val) {
        if(val && config[key]) {
            return config[key] = val
        }
        return config[key]
    }

	globalData(key, val) {
		try {
			if ((val || this.isString(val) || val === false) && (app.globalData[key] || this.isString(app.globalData[key]) || app.globalData[key] === null || app.globalData[key] === false)) {
				return app.globalData[key] = val
			}
			if (key.indexOf('Temp') != -1) {
				let temp = app.globalData[key]
				app.globalData[key] = null
				return temp	
			}
			return app.globalData[key]
		}
		catch (err) {
			console.error('set/get global data failed')
		}
	}

	addExtDataNeed(name) {
		let needExtData = this.globalData('needExtData')
		needExtData.push(name)
		this.globalData('needExtData', needExtData)
	}

	showLoadingPanel(p, tip = '请稍候') {
		p.setData({
			loadingPanel: { tip }
		})
	}

	hideLoadingPanel(p) {
		p.setData({
			loadingPanel: { tip: false }
		})
	}

	showPanelErr(obj) {
		let { p, icon = 'failed', msg = '加载失败', use = '请下拉重试', title = '出错啦' } = obj
		wx.hideNavigationBarLoading()
		wx.stopPullDownRefresh()
		wx.setNavigationBarTitle({
			title,
		})
		p.setData({ err: { icon, msg, use } })
	}

	hidePanelErr(p) {
		if (!p.data.err)
			return
		p.setData({ err: null })
	}

	showLoadingBox(p) {
		p.setData({ loadingBox: { show: true } })
	}

	hideLoadingBox(p) {
		p.setData({ loadingBox: { show: false } })
	}

	tip(obj) {
		let { p, text, timeout = 3000, done } = obj
		clearTimeout(this.tipCloseTimer)
		p.setData({
			tip: {
				text: false
			}
		})
		p.setData({
			tip: {
				text,
				timeout
			}
		})
		this.tipCloseTimer = setTimeout(() => {
			p.setData({
				tip: {
					text: false
				}
			})
			if (done) {
				done()
			}
		}, timeout)
	}

    success(successMsg = '操作成功', timeout = 1500) {
        wx.showToast({
            title: successMsg,
            icon: 'success',
            duration: timeout,
            mask: true
        })
    }

	error(errTitle = '发生错误', errMsg, callback) {
		wx.showModal({
			title: errTitle,
			content: errMsg,
			cancelText: '反馈问题',
			cancelColor: '#ff6759',
			success: (res) => {
				if (res.cancel) {
					wx.redirectTo({
						url: '/pages/feedback/feedback',
					})
					return
				}
				if (callback)
					callback()
			},
			fail: () => {
				if (callback)
					callback()
			}
		})
	}

	modal(title = '系统提示', msg, callback) {
		wx.showModal({
			title,
			content: msg,
			showCancel: false,
			success: (res) => {
				if (callback)
					callback()
			},
			fail: () => {
				if (callback)
					callback()
			}
		})
	}

    iconBox(icon, title = '系统提示') {
        wx.showToast({
            title: title,
            icon: 'success',
            image: `${icon}.png`
        })
    }

    loading(title) {
        wx.showToast({
            title: title,
            icon: 'loading',
            duration: 60000,
            mask: true
        })
    }

	hideLoading() {
		wx.hideToast()
	}

	sign(data = {}) {
		let signStr = ''
		let keys = Object.keys(data)
		keys = keys.sort()
		for (let key of keys) {
			if (!isNaN(key)) {
				continue
			}
			else if (Object.prototype.toString.call(data[key]) == '[object Object]' || Object.prototype.toString.call(data[key]) == '[object Array]') {
				signStr += `&${key}=${JSON.stringify(data[key])}`
			}
			else if (typeof (data[key]) != 'undefined')
				signStr += `&${key}=${encodeURI(data[key])}`
		}
		signStr += `&token=${this.globalData('token')}`
		signStr = signStr.substring(1)
		console.log('sign str is ' + signStr)
		return crypto.md5(signStr)
	}

	buildSignUrl(uri = config.requestDomain, data = {}) {
		data.grantType = 'token'
		data.timestamp = this.timestamp()
		data.nonce = this.createRandomStr(10)
		uri += '?'
		for (let key in data) {
			uri += `${key}=${data[key]}&`
		}
		uri += `sign=${this.sign(data)}`
		return config.requestDomain + uri
	}

	stringToBuffer(str) {
		const buf = new ArrayBuffer(str.length)
		let arrayBuffer = new Uint8Array(buf)
		for (let i = 0; i < str.length; i++) {
			arrayBuffer[i] = str.charCodeAt(i);
		}
		return arrayBuffer
	}

	request(obj, retryNum) {
		const key = this.createRandomStr(16)
		console.log('加密key', key)
		let { source, header, method, uri, data = {}, success, fail, complete = () => { }, sign, encrypt = true } = obj
		try {
			for(let match of uri.match(/:\w+/g) || []) {
				const key = match.substring(1)
				if (data[key])
					uri = uri.replace(new RegExp(`:${key}`, 'g'), data[key])
			}
			let requestData = {}
			if (encrypt && data) {
				console.log('加密数据', data)
				const iv = this.createRandomStr(16)
				requestData.iv = wx.arrayBufferToBase64(this.stringToBuffer(iv))
				requestData.encryptData = crypto.encryptAES(JSON.stringify(data), key, iv)
				requestData.encryptKey = crypto.encryptRSA(key, config.publicKey)
			}
			else
				requestData = data
			if (sign !== false) {
				Object.assign(requestData, {
					grantType: 'token',
					timestamp: this.timestamp(),
					nonce: this.createRandomStr(10),
					session: this.globalData('session')
				})
				requestData.sign = this.sign(requestData)
			}
			this.requestSources.push(source)
			wx.request({
				url: config.requestDomain + uri,
				header,
				method: method,
				dataType: 'json',
				data: requestData,
				success: (res) => {
					console.log(this.requestSources, source)
					const sIndex = this.requestSources.indexOf(source)
					if (sIndex == -1)
						return
					this.requestSources.splice(sIndex, 1)
					if (res.data.status == 'success') {
						//如果存在加密数据则解密先
						if (res.data.encryptData && res.data.iv) {
							const decryptData = crypto.decryptAES(res.data.encryptData, key, res.data.iv)
							const temp = this.isJson(decryptData)
							res.data = temp ? temp : decryptData
							console.log('解密数据：', res.data)
						}
						success(res.data.data)
						complete()
					}
					else {
						let { code, msg } = res.data
						console.error(code, msg)
						if (['-3066', '-3057', '-1004'].indexOf(code) != -1 && uri != 'studio/checkLogin' && uri != 'studio/logout') {
							this.modal('登录过期', '您的登录状态已经过期啦，需要重新登录~', () => {
								wx.switchTab({
									url: '/pages/my/my',
								})
							})
							this.globalData('logined', false)
							this.globalData('userData', null)
							this.globalData('commonData', null)
							wx.clearStorage()
							complete()
						}
						else {
							fail(code, msg)
							complete()
						}
					}
				},
				fail: (err) => {
					const sIndex = this.requestSources.indexOf(source)
					if (sIndex == -1)
						return
					this.requestSources.splice(sIndex, 1)
					console.error(err)
					fail('-2', 'request failed', err)
					complete()
				}
			})
		}
		catch (err) {
			console.error('create request failed', err)
			fail('-1', 'create request failed')
			complete()
		}
	}

	uploadFile(obj, retryNum) {
		const { uri, data, name, file, sign, success, fail } = obj
		try {
			if (sign !== false) {
				data.grantType = 'token'
				data.timestamp = this.timestamp()
				data.nonce = this.createRandomStr(10)
				data.sign = this.sign(data)
			}
			wx.uploadFile({
				url: config.requestDomain + uri,
				filePath: file,
				name,
				formData: data,
				success: (res) => {
					const result = this.isJson(res.data)
					if (!data) {
						fail('-2', 'upload failed')
						return
					}
					if (result.status == 'success') {
						success(result.data)
					}
					else {
						let { code, msg } = result
						console.error(code, msg)
						if (code == '-1004' && uri != 'user/checkLogin') {
							this.startLogin({
								success: () => {
									console.log(retryNum)
									if (!retryNum && retryNum != 0)
										retryNum = 0
									else if (retryNum == 2) {
										fail(code, msg)
										return
									}
									console.log(retryNum)
									delete obj.data.sign
									this.uploadFile(obj, retryNum++)
								},
								fail: () => {
									fail(code, msg)
								}
							})
						}
						else
							fail(code, msg)
					}
				},
				fail: (err) => {
					console.error(err)
					fail('-2', 'upload failed')
				}
			})
		}
		catch (err) {
			console.error('create upload failed', err)
			fail('-1', 'create upload failed')
		}
	}

	goPage(pageName, data, notBack = false) {
		let uri = ''
		for (let key in data) {
			uri += `&${key}=${data[key]}`
		}
		uri = uri != '' ? ('?' + uri.substr(1, uri.length - 1)) : ''
		if (!notBack)
			wx.navigateTo({
				url: `/pages/${pageName}/${pageName}${uri}`,
				fail: (err) => {
					console.error(err)
					this.error('跳转失败', '悲剧了，跳转到该页面失败，请返回上一层')
				}
			})
		else
			wx.redirectTo({
				url: `/pages/${pageName}/${pageName}${uri}`,
				fail: (err) => {
					console.error(err)
					this.error('跳转失败', '悲剧了，跳转到该页面失败，请返回上一层')
				}
			})
	}

	goLastPage(data) {
		const pages = getCurrentPages()
		if(pages.length < 2)
			return
		if(!pages[pages.length - 2].temp)
			pages[pages.length - 2].temp = {}
		Object.assign(pages[pages.length - 2].temp, data)
		wx.navigateBack()
	}

    getRange(lat1 = 0, lng1 = 0, lat2 = 0, lng2 = 0, unitConvert = false) {
        const r = 6378137
        let rad1 = lat1 * Math.PI / 180.0
        let rad2 = lat2 * Math.PI / 180.0
        let a = rad1 - rad2
        let b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0
        let range = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) * Math.cos(rad2) * Math.pow(Math.sin(b / 2), 2)))
        if (unitConvert) {
            if (range < 1000) {
                range = `${parseInt(range)}m`
            }
            else {
                range = `${parseFloat((range / 1000).toFixed(1))}km`
            }
        }
        return range
    }

	timestamp() {
		return parseInt(Date.now() / 1000)
	}

	timestamp2str(timestamp, year = true, month = true, day = true, hours, min, second, split = '-') {
		let date = new Date(timestamp * 1000)
		let _year = `${year ? (date.getFullYear() > 9 ? date.getFullYear() : '0' + date.getFullYear()) + split : ''}`
		let _month = `${month ? (date.getMonth() + 1 > 9 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)) + split : ''}`
		let _day = `${day ? (date.getDate() > 9 ? date.getDate() : '0' + date.getDate()) : ''} `
		let _hours = `${hours ? (date.getHours() > 9 ? date.getHours() : '0' + date.getHours()) + ':' : ''}`
		let _min = `${min ? (date.getMinutes() > 9 ? date.getMinutes() : '0' + date.getMinutes()) + ':' : ''}`
		let _second = `${second ? (date.getSeconds() > 9 ? date.getSeconds() : '0' + date.getSeconds()) : ''}`
		return `${_year}${_month}${_day}${_hours}${_min}${_second}`
	}

    isString(val) {
        return Object.prototype.toString.call(val) === '[object String]'
    }

	createRandomStr(len, material) {
        let randomStr = ''
		material = material || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
        for (var i = 0; i < len; i++) {
            //添加在素材字符串随机找到的字符
			randomStr += material[parseInt(Math.random() * material.length)];
        }
        return randomStr
    }

	md5(data) {
		return crypto.md5(data)
	}

	errorHandler(code = '-1', msg = '未知错误') {
		wx.hideToast()
		wx.hideNavigationBarLoading()
		wx.stopPullDownRefresh()
		if (app.errList[code])
			return `[${code}] ${app.errList[code]}`
		else
			return `[${code}] ${msg}`
	}

	checkCommonData({sync, success, fail}) {
		if (!sync && this.globalData('commonData')) {
			success(this.globalData('commonData'))
			return
		}
		this.request({
			source: 'util',
			method: sync ? 'put' : 'get',
			uri: 'studio/common',
			success: (commonData) => {
				this.globalData('commonData', commonData)
				wx.setStorage({
					key: 'commonData',
					data: commonData
				})
				success(commonData)
			},
			fail
		})
	}

	checkLogin(callback) {
		let { success, fail, scene } = callback
		if (this.globalData('loginChecked')) {
			fail()
			return
		}	
		if (this.globalData('logined') && this.globalData('userData')) {
			success(this.globalData('userData'))
			return
		}
		let session = this.globalData('session')
		let token = this.globalData('token')
		if (!session || !token || token == '' || session == '') {
			this.globalData('loginChecked', true)
			fail()
			return
		}
		console.log(`now session is ${session}`)
		this.request({
			source: 'util',
			method: 'get',
			uri: 'studio/checkLogin',
			success: (userData) => {
				wx.stopPullDownRefresh()
				const _userData = wx.getStorageSync('userData')
				Object.assign(_userData, userData)
				this.globalData('userData', _userData)
				this.globalData('logined', true)
				wx.setStorage({
					key: 'userData',
					data: _userData
				})
				success(_userData)
			},
			fail: (code, msg) => {
				if(code != '-1004')
					this.error('登录失败', '自动登录失败，可能网络问题，请重新登录或重新打开微科创')
				this.globalData('logined', false)
				this.globalData('loginChecked', true)
				fail()
			}
		})
	}

	bindMsgBack(type, callback) {
		this.msgBack[type] = callback
	}

	uniteMsgBack(type) {
		delete this.msgBack[type]
	}

	wsHandler(res) {
		if(res == '@heart') {
			this.wsConnTryCount = 0
			return
		}	
		const msg = JSON.parse(res)
		console.log(msg)
		const { id, type, data, time } = msg
		if (!id) {
			switch(type) {
				case 'authError':
					console.error('ws auth failed')
				break
				case 'serverError':
					console.error('ws server error')
				break
				case 'otherDevLogined':
					this.globalData('notReconnect', true)
					console.error('other device is connect')
				break
				default:
					console.error(`unknown error:${type}`)
			}
			return
		}
		let newMsgList = this.globalData('newMsgList')
		if (!newMsgList instanceof Array)
			newMsgList = []
		newMsgList.push({ id, type, data, time })
		this.globalData('newMsgList', newMsgList)
		wx.setStorage({
			key: 'newMsgList',
			data: newMsgList
		})
		for (let t in this.msgBack)
			this.msgBack[t](msg)
	}

	uploadOSSFile({ path, dir = '', name, prefix = '', success, fail }) {
		name = name || this.createRandomStr(16)
		this.request({
			source: 'other',
			method: 'get',
			uri: 'sys/ossData',
			success: ({ accessKeyId, policy, signature }) => {
				wx.uploadFile({
					url: this.config('upload').ossDomain,
					filePath: path,
					name: 'file',
					formData: {
						name: path,
						key: dir + name + prefix,
						policy,
						OSSAccessKeyId: accessKeyId,
						signature,
						success_action_status: '200'
					},
					success: (res) => {
						console.log(res)
						success(`${this.config('upload').ossDomain}/${dir}${name}${prefix}`)
					},
					fail: (err) => {
						console.error(err)
						fail(-4, 'upload file to oss failed')
					}
				})
			},
			fail
		})
	}

}

module.exports = new util